/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "service_impl.h"

#include "common_utils.h"
#include "iremote_object.h"
#include "loghelper.h"
#include "reader_impl.h"
#include "se_common_exception.h"
#include "se_timer.h"
#include "service_manager.h"
#include "secure_element_agent.h"
#include "se_hianalytics.h"

namespace OHOS {
namespace SE {
uint32_t ServiceImpl::unloadSaTimerId{0};
/**
 * @brief Died callback for caller application.
 */
class ServiceImpl::CallerApplicationDeathReceiver : public IRemoteObject::DeathRecipient {
public:
    explicit CallerApplicationDeathReceiver(wptr<ServiceImpl> serviceImpl,
                                            const std::string &callingUniqueTag)
        : serviceImpl_(serviceImpl),
          callingUniqueTag_(callingUniqueTag)

    {
        InfoLog("ServiceImpl::CallerApplicationDeathReceiver");
    }

    // close all has opened channel and unload service if no client bound to the service
    void OnRemoteDied(const wptr<IRemoteObject>& object) override
    {
        std::lock_guard<std::mutex> lock(mutex_);
        InfoLog("callToken died.");
        if (object == nullptr) {
            ErrorLog("CallerApplicationDeathReceiver, IRemoteObject expired.");
            return;
        }
        if (serviceImpl_.promote() == nullptr) {
            ErrorLog("CallerApplicationDeathReceiver, serviceImpl_ is nullptr.");
            return;
        }
        serviceImpl_.promote()->OnRemoteDied(callingUniqueTag_);
    }

private:
    DISALLOW_COPY_AND_MOVE(CallerApplicationDeathReceiver);

    // the sa manager.
    wptr<ServiceImpl> serviceImpl_;

    // application callingUniqueTag
    std::string callingUniqueTag_;

    // mutext lock for service.
    std::mutex mutex_{};
};

ServiceImpl::ServiceImpl(std::weak_ptr<ServiceManager> service,
                         std::vector<SeNameAndAgentMap>& table)
    : serviceManager_(service), secureElementAgents_(table)
{
    InfoLog("ServiceImpl secure element size = %{public}lu", table.size());
}

ServiceImpl::~ServiceImpl()
{
    InfoLog("ServiceImpl::~ServiceImpl.");
}

void ServiceImpl::OnRemoteDied(const std::string &callingUniqueTag)
{
    callingUniqueTag_ = callingUniqueTag;
    Shutdown();
    callingUniqueTag_.clear();
}

ErrorCode ServiceImpl::GetReaders(std::vector<std::string>& readerList)
{
    std::lock_guard<std::mutex> lock(mutex_);
    std::vector<std::string> readers{};
    for (SeNameAndAgentMap agent : secureElementAgents_) {
        // push back the name of secure element.
        readers.push_back(agent.first);
    }
    readerList = readers;
    return ErrorCode::SE_SUCCESS;
}

ErrorCode ServiceImpl::GetReaderInterface(const std::string &readerName, sptr<OMAPI::ISecureElementReader>& iface)
{
    std::lock_guard<std::mutex> lock(mutex_);
    std::weak_ptr<SecureElementAgent> agent{};
    for (SeNameAndAgentMap item : secureElementAgents_) {
        if (item.first.compare(readerName) == 0) {
            agent = item.second;
            break;
        }
    }
    if (agent.expired()) {
        WarnLog("GetReaderInterface, agent unmatched for name");
    }
    iface = agent.expired() ? nullptr : new ReaderImpl(agent);
    return ErrorCode::SE_SUCCESS;
}

ErrorCode ServiceImpl::IsNfcEventAllowed(const std::string &readerName, const std::string &aid,
    std::vector<std::string> bundleNames, std::vector<bool>& allowedStatus)
{
    allowedStatus = std::vector<bool>();
    return ErrorCode::SE_SUCCESS;
}

void ServiceImpl::UnloadSESA()
{
    InfoLog("enter");
    sptr<ISystemAbilityManager> samgr =
        SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (samgr == nullptr) {
        ErrorLog("get system ability manager failed!");
        return;
    }
    int32_t ret = samgr->UnloadSystemAbility(SE_MANAGER_SYS_ABILITY_ID);
    if (ret != ERR_NONE) {
        ErrorLog("Failed to unload system ability, SA Id = [%{public}d], ret = [%{public}d].",
            SE_MANAGER_SYS_ABILITY_ID,
            ret);
    }
    SeHiAnalytics::GetInstance().WriteLogWithoutCondition();
}

ErrorCode ServiceImpl::Shutdown()
{
    std::lock_guard<std::mutex> lock(mutex_);
    std::string callingUniqueTag = (callingUniqueTag_.empty()) ? CommonUtils::GetUniqueTag() : callingUniqueTag_;
    InfoLog("%{public}lu clients are being bound to the service.", applicationListeners_.size());
    for (auto agent : secureElementAgents_) {
        agent.second->ReleaseByCallingTokenId(callingUniqueTag);
    }
    applicationListeners_.erase(callingUniqueTag);

    if (applicationListeners_.size() == 0) {
        // register timeout callback to unload SA
        TimeOutCallback timeoutCallback = []() { ServiceImpl::UnloadSESA(); };
        if (unloadSaTimerId != 0) {
            SETimer::GetInstance()->UnRegister(unloadSaTimerId);
            unloadSaTimerId = 0;
        }
        SETimer::GetInstance()->Register(timeoutCallback, unloadSaTimerId, TIMEOUT_UNLOAD_SE_SA);
    }
    InfoLog("erase callToken success.");
    return ErrorCode::SE_SUCCESS;
}

ErrorCode ServiceImpl::RegisterCallerListener(const sptr<IRemoteObject> &obj)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (unloadSaTimerId != 0) {
        SETimer::GetInstance()->UnRegister(unloadSaTimerId);
        unloadSaTimerId = 0;
    }

    std::string callingUniqueTag = CommonUtils::GetUniqueTag();
    if (!IsRegisterAppListener(callingUniqueTag)) {
        InfoLog("add listener");
        applicationListeners_.insert(std::make_pair(callingUniqueTag,
            new CallerApplicationDeathReceiver(this, callingUniqueTag)));
        obj->AddDeathRecipient(applicationListeners_[callingUniqueTag]);
    }
    InfoLog("UniqueTag = %{public}s AddDeathRecipient, size[%{public}lu]",
        callingUniqueTag.c_str(),
        applicationListeners_.size());
    return ErrorCode::SE_SUCCESS;
}

bool ServiceImpl::IsRegisterAppListener(const std::string &callingUniqueTag)
{
    auto iter = applicationListeners_.begin();
    while (iter != applicationListeners_.end()) {
        if (callingUniqueTag == iter->first) {
            InfoLog("%{public}s has register.", callingUniqueTag.c_str());
            return true;
        }
        ++iter;
    }
    return false;
}
}  // namespace SE
}  // namespace OHOS
